﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2021-2025 LiLong                                              **
**  This file is part of CommonUnits.                                           **
**                                                                              **
**  CommonUnits is free software: you can redistribute it and/or modify         **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  CommonUnits is distributed in the hope that it will be useful,              **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with CommonUnits.  If not, see <https://www.gnu.org/licenses/>.       **
**********************************************************************************/
#pragma once

#include <cstddef>
#include <ranges>
#include <string>

namespace CU
{
template<typename _Char, std::size_t N>
class BasicFixedString
{
public:
    using CharT          = _Char;
    using iterator       = CharT*;
    using const_iterator = const CharT*;
    constexpr BasicFixedString(CharT ch) noexcept { m_buffer[0] = ch; }
    constexpr BasicFixedString(const CharT (&buffer)[N + 1]) noexcept
    {
        if constexpr (N != 0)
            for (std::size_t i = 0; i < N; i++)
            {
                m_buffer[i] = buffer[i];
            }
    }
    constexpr BasicFixedString(const std::array<CharT, N>& span) noexcept
    {
        if constexpr (N != 0)
            for (std::size_t i = 0; i < N; i++)
            {
                m_buffer[i] = span[i];
            }
    }

    [[nodiscard]] constexpr bool empty() const noexcept { return !N; }
    [[nodiscard]] constexpr std::size_t size() const noexcept { return N; }
    [[nodiscard]] constexpr const CharT* data() const noexcept { return m_buffer; }
    [[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); }
    [[nodiscard]] constexpr const CharT& operator[](std::size_t index) const noexcept { return data()[index]; }
    [[nodiscard]] constexpr CharT operator[](std::size_t index) noexcept { return data()[index]; }

    [[nodiscard]] constexpr iterator begin() noexcept { return data(); }
    [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); }
    [[nodiscard]] constexpr iterator end() noexcept { return data() + size(); }
    [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); }

    template<std::size_t N2>
    [[nodiscard]] constexpr friend BasicFixedString<CharT, N + N2> operator+(const BasicFixedString& lhs,
                                                                             const BasicFixedString<CharT, N2>& rhs) noexcept
    {
        CharT txt[N + N2 + 1] {};
        for (size_t i = 0; i != N; ++i)
            txt[i] = lhs[i];
        for (size_t i = 0; i != N2; ++i)
            txt[N + i] = rhs[i];
        return BasicFixedString<CharT, N + N2>(txt);
    }

    [[nodiscard]] constexpr bool operator==(const BasicFixedString& other) const
    {
        return std::equal(begin(), end(), other.begin(), other.end());
    }

    template<std::size_t N2>
    [[nodiscard]] constexpr bool operator==(const BasicFixedString<CharT, N2>&) const
    {
        return false;
    }

    template<std::size_t N2>
    [[nodiscard]] friend constexpr auto operator<=>(const BasicFixedString& lhs, const BasicFixedString<CharT, N2>& rhs)
    {
        return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
    }

    [[nodiscard]] inline operator std::basic_string<CharT>() const { return std::basic_string<CharT>(m_buffer, N); }
    [[nodiscard]] constexpr operator std::basic_string_view<CharT>() const { return std::basic_string_view<CharT>(m_buffer, N); }

public:
    CharT m_buffer[N + 1] {};
};

template<typename CharT, std::size_t N>
BasicFixedString(const CharT (&str)[N]) -> BasicFixedString<CharT, N - 1>;

template<typename CharT>
BasicFixedString(CharT) -> BasicFixedString<CharT, 1>;

template<typename CharT, std::size_t N>
BasicFixedString(const std::array<CharT, N>& span) -> BasicFixedString<CharT, N>;

template<typename CharT, class Traits, std::size_t N>
inline std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const BasicFixedString<CharT, N>& txt)
{
    return os << txt.c_str();
}
} // namespace CU
